/******************************************************************************

Welcome to GDB Online.
GDB online is an online compiler and debugger tool for C, C++, Python, Java, PHP, Ruby, Perl,
C#, VB, Swift, Pascal, Fortran, Haskell, Objective-C, Assembly, HTML, CSS, JS, SQLite, Prolog.
Code, Compile, Run and Debug online from anywhere in world.

*******************************************************************************/
#include <stdio.h>
#include <iostream>

using namespace std;

// 模板代码的组织结构
// 对于一个普通类：
// 实际项目中需要把类定义和类实现都分别放在.h和.cpp文件中

// 对于一个类模板来说
// 首先，类模板定义本身是不会导致编译器生成相关实例化的代码的
// 只有在程序中使用了类模板的时候，编译器才会去实例化该类模板
// 同样对于函数模板来说，也是一样的，只有在使用的时候编译器才会去判断是否有必要实例化该函数


// 如果把类模板像类一样分别在.h和.cpp中定义和实现，那么在编译运行的时候就会报编译链接错误
// 因为main主函数是位于我们项目的源文件中，编译器在编译我们整个项目的时候，
// 是针对我们每个.cpp文件作为一个编译单元来进行编译，
// 所以编译器要首先来编译我们main函数所在的编译单元（.cpp）编译器在编译的时候，遇到了我们的模板实例化这行代码
// 然后编译器就会根据这个类模板的定义就实例化出来了这个类T<int>，
// 但是因为这个模板类所代表的这个源文件（编译单元）中它找不到这个类模板的实现代码（也就是main主函数所在的.cpp文件中找不到模板类的实现代码）
// 实现代码被我们写在了另一个.cpp文件中，因此编译器在编译这个main这个编译单元的时候，是实例化不出来这个类的
// 但是编译器并不会因为实例化不出来这个类他就报错，编译器会假设你在这个.cpp中没有这个类模板的实例化
// 但是你可能在项目中有很多的编译单元，其他编译单元中可能会有这个类的模板的实例化，
// 所以编译器不会再编译的时候报错找不到T<int>这个类，
// 那么在什么时候编译器会判断到底实没实例化这个类呢?
// 是在链接的时候，我们都知道C++项目都是先编译后链接，然后生成可执行文件
// 那么编译完了，在链接阶段，编译器才会真正的去找这个类，如果找不到这个类的实例，那么编译器就会报错
// 所以在我们当前这种，只在main中实例化这个T<int>，编译器就找不到其他的这个T<int>的实例化（因为也就只有这里有实例化）
// 说白了：就是因为编译器在链接阶段找不到类的实现代码，所以报错

// 当然还有另外一种代码组织方式，就是假设你在多个.cpp文件中都生成了T<int>这个类
// 编译器在链接时就会选择其中一个，然后把其他的丢弃掉，这叫 贪婪实例化

// 总结：
// 类模板的定义和实现通常都放在.h头文件中，
// 而不能把定义和实现分别放在.h和.cpp文件中（把定义和实现分开）


// 模板的显式实例化，模板声明，代码组织结构
// 我们知道，模板只有在使用的时候，才会被实例化出来，但是现在有这样一个问题，
// 在实例项目中，我们不可能只有一个.cpp文件，可能会有多个.cpp文件

// template A<float>;  这个叫“实例化定义”，一般只有一个.cpp文件里这样写，编译器为其生成代码
// extern template A<float>;  这个叫“实例化声明”，在其他的.cpp源文件中都这样写
// 编译器在遇到这种extern模板实例化声明，就不会在当前的.cpp源文件中生成一个该实例化版本的代码
// extern就是告诉编译器，在其他源文件中已经有了该类模板的针对A<float>的实例化版本
// 本行一般写在.cpp源文件的上面位置

// template class A<float>; 这个叫“实例化定义”，。一般只有一个.cpp文件中这样写，编译器为其生成代码
// template void func(int& i, int& j); 函数模板实例化定义，编译器会为其生成实例化代码
// extern template void func(int& i, int& j); 函数模板实例化声明，编译器会为其生成实例化代码

// 总结：使用VS2017， vs2019，不推荐使用类模板显示实例化

namespace _nmsp1
{
    // 
    
    void func()
    {
        
    }
}


int main()
{
    _nmsp1::func();

    return 0;
}

